home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / daemons / ftpd / ftpd.c < prev    next >
C/C++ Source or Header  |  1992-08-11  |  25KB  |  1,263 lines

  1. /*
  2.  * Copyright (c) 1985 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that this notice is preserved and that due credit is given
  7.  * to the University of California at Berkeley. The name of the University
  8.  * may not be used to endorse or promote products derived from this
  9.  * software without specific prior written permission. This software
  10.  * is provided ``as is'' without express or implied warranty.
  11.  */
  12.  
  13. #ifndef lint
  14. char copyright[] =
  15. "@(#) Copyright (c) 1985 Regents of the University of California.\n\
  16.  All rights reserved.\n";
  17. #endif /* not lint */
  18.  
  19. #ifndef lint
  20. static char sccsid[] = "@(#)ftpd.c    5.11 (Berkeley) 3/14/88";
  21. #endif /* not lint */
  22.  
  23. /*
  24.  * FTP server.
  25.  */
  26. #include <sys/param.h>
  27. #include <sys/stat.h>
  28. #include <sys/ioctl.h>
  29. #include <sys/socket.h>
  30. #include <sys/file.h>
  31. #include <sys/wait.h>
  32.  
  33. #include <netinet/in.h>
  34.  
  35. #include <arpa/ftp.h>
  36. #include <arpa/inet.h>
  37. #include <arpa/telnet.h>
  38.  
  39. #include <stdio.h>
  40. #include <signal.h>
  41. #include <pwd.h>
  42. #include <setjmp.h>
  43. #include <netdb.h>
  44. #include <errno.h>
  45. #include <strings.h>
  46. #include <syslog.h>
  47. #include <varargs.h>
  48.  
  49. /*
  50.  * File containing login names
  51.  * NOT to be used on this machine.
  52.  * Commonly used to disallow uucp.
  53.  */
  54. #define    FTPUSERS    "/etc/ftpusers"
  55.  
  56. extern    int errno;
  57. extern    char *sys_errlist[];
  58. extern    char *crypt();
  59. extern    char version[];
  60. extern    char *home;        /* pointer to home directory for glob */
  61. extern    FILE *popen(), *fopen(), *freopen();
  62. extern    int  pclose(), fclose();
  63. extern    char *getline();
  64. extern    char cbuf[];
  65. extern    char *asctime();
  66. extern char *ctime();
  67.  
  68. struct    sockaddr_in ctrl_addr;
  69. struct    sockaddr_in data_source;
  70. struct    sockaddr_in data_dest;
  71. struct    sockaddr_in his_addr;
  72.  
  73. static char     *ftpdlog = "/usr/adm/ftpdlog";
  74.  
  75. void log();
  76.  
  77. int    data;
  78. jmp_buf    errcatch, urgcatch;
  79. int    logged_in;
  80. struct    passwd *pw;
  81. int    debug;
  82. int    timeout = 900;    /* timeout after 15 minutes of inactivity */
  83. int    logging;
  84. int    guest;
  85. int    wtmp;
  86. int    type;
  87. int    form;
  88. int    stru;            /* avoid C keyword */
  89. int    mode;
  90. int    usedefault = 1;        /* for data transfers */
  91. int    pdata;            /* for passive mode */
  92. int    unique;
  93. int    transflag;
  94. char    tmpline[7];
  95. char    hostname[32];
  96. char    remotehost[32];
  97. char    homepathbuf[MAXPATHLEN+1];
  98.  
  99. /*
  100.  * Timeout intervals for retrying connections
  101.  * to hosts that don't accept PORT cmds.  This
  102.  * is a kludge, but given the problems with TCP...
  103.  */
  104. #define    SWAITMAX    90    /* wait at most 90 seconds */
  105. #define    SWAITINT    5    /* interval between retries */
  106.  
  107. int    swaitmax = SWAITMAX;
  108. int    swaitint = SWAITINT;
  109.  
  110. int    lostconn();
  111. int    myoob();
  112. FILE    *getdatasock(), *dataconn();
  113.  
  114. main(argc, argv)
  115.     int argc;
  116.     char *argv[];
  117. {
  118.     int addrlen, on = 1;
  119.     long pgid;
  120.     char *cp;
  121.  
  122.     addrlen = sizeof (his_addr);
  123.     if (getpeername(0, &his_addr, &addrlen) < 0) {
  124.         syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
  125.         exit(1);
  126.     }
  127.     addrlen = sizeof (ctrl_addr);
  128.     if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) {
  129.         syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
  130.         exit(1);
  131.     }
  132.     data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
  133.     debug = 0;
  134.     openlog("ftpd", LOG_PID, LOG_DAEMON);
  135.     argc--, argv++;
  136.     while (argc > 0 && *argv[0] == '-') {
  137.         for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
  138.  
  139.         case 'v':
  140.             debug = 1;
  141.             break;
  142.  
  143.         case 'd':
  144.             debug = 1;
  145.             break;
  146.  
  147.         case 'l':
  148.             logging = 1;
  149.             break;
  150.  
  151.         case 't':
  152.             timeout = atoi(++cp);
  153.             goto nextopt;
  154.             break;
  155.  
  156.         default:
  157.             fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
  158.                  *cp);
  159.             break;
  160.         }
  161. nextopt:
  162.         argc--, argv++;
  163.     }
  164.     (void) freopen("/dev/null", "w", stderr);
  165.     (void) signal(SIGPIPE, lostconn);
  166.     (void) signal(SIGCHLD, SIG_IGN);
  167.     if (signal(SIGURG, myoob) < 0) {
  168.         syslog(LOG_ERR, "signal: %m");
  169.     }
  170.     /* handle urgent data inline */
  171. #ifdef SO_OOBINLINE
  172.     if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) {
  173.         syslog(LOG_ERR, "setsockopt: %m");
  174.     }
  175. #endif SO_OOBINLINE
  176.     pgid = getpid();
  177.     if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) {
  178.         syslog(LOG_ERR, "ioctl: %m");
  179.     }
  180.     dolog(&his_addr,0);
  181.     /* do telnet option negotiation here */
  182.     /*
  183.      * Set up default state
  184.      */
  185.     logged_in = 0;
  186.     data = -1;
  187.     type = TYPE_A;
  188.     form = FORM_N;
  189.     stru = STRU_F;
  190.     mode = MODE_S;
  191.     tmpline[0] = '\0';
  192.     (void) gethostname(hostname, sizeof (hostname));
  193.     reply(220, "%s FTP server (%s) ready.",
  194.         hostname, version);
  195.     for (;;) {
  196.         (void) setjmp(errcatch);
  197.         (void) yyparse();
  198.     }
  199. }
  200.  
  201. lostconn()
  202. {
  203.  
  204.     if (debug)
  205.         syslog(LOG_DEBUG, "lost connection");
  206.     dologout(-1);
  207. }
  208.  
  209. pass(passwd)
  210.     char *passwd;
  211. {
  212.     char *xpasswd, *savestr();
  213.     static struct passwd save;
  214.  
  215.     if (logged_in || pw == NULL) {
  216.         reply(503, "Login with USER first.");
  217.         return;
  218.     }
  219.     if (!guest) {        /* "ftp" is only account allowed no password */
  220.         xpasswd = crypt(passwd, pw->pw_passwd);
  221.         /* The strcmp does not catch null passwords! */
  222.         if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
  223.             reply(530, "Login incorrect.");
  224.             pw = NULL;
  225.             return;
  226.         }
  227.     } else {
  228.         dolog(&his_addr,1);
  229.         log("Anonymous ftp from %s to %s\n", passwd, hostname);
  230.     }
  231.     setegid(pw->pw_gid);
  232.     initgroups(pw->pw_name, pw->pw_gid);
  233.     if (chdir(pw->pw_dir)) {
  234.         reply(530, "User %s: can't change directory to %s.",
  235.             pw->pw_name, pw->pw_dir);
  236.         goto bad;
  237.     }
  238.  
  239.     /* grab wtmp before chroot */
  240.     wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
  241. /*
  242.  * Sprite doesn't support chroot.  For now, fake chroot.
  243.  */
  244. #ifndef sprite
  245.     if (guest && chroot(pw->pw_dir) < 0) {
  246. #else
  247.     if (guest && (chdir("pub") || !getwd(homepathbuf))) {
  248. #endif
  249.         reply(550, "Can't set guest privileges.");
  250.         if (wtmp >= 0) {
  251.             (void) close(wtmp);
  252.             wtmp = -1;
  253.         }
  254.         goto bad;
  255.     }
  256.     if (!guest)
  257.         reply(230, "User %s logged in.", pw->pw_name);
  258.     else if (!strcmp(passwd,"guest")||!strcmp(passwd,"anonymous")) {
  259.         reply(230, "Afraid to use your real name?  Guest login ok, access restrictions apply.");
  260.     } else {
  261.         reply(230, "Guest login ok, access restrictions apply.");
  262.     }
  263.     logged_in = 1;
  264.     dologin(pw);
  265.     seteuid(pw->pw_uid);
  266.     /*
  267.      * Save everything so globbing doesn't
  268.      * clobber the fields.
  269.      */
  270.     save = *pw;
  271.     save.pw_name = savestr(pw->pw_name);
  272.     save.pw_passwd = savestr(pw->pw_passwd);
  273.     save.pw_gecos = savestr(pw->pw_gecos);
  274.     save.pw_dir = savestr(pw->pw_dir);
  275.     save.pw_shell = savestr(pw->pw_shell);
  276.     pw = &save;
  277.     home = pw->pw_dir;        /* home dir for globbing */
  278.     return;
  279. bad:
  280.     seteuid(0);
  281.     pw = NULL;
  282. }
  283.  
  284. char *
  285. savestr(s)
  286.     char *s;
  287. {
  288.     char *malloc();
  289.     char *new = malloc((unsigned) strlen(s) + 1);
  290.     
  291.     if (new != NULL)
  292.         (void) strcpy(new, s);
  293.     return (new);
  294. }
  295.  
  296. #define TMPLEN 10000
  297. static char tmpbuf[TMPLEN+1];
  298. /*
  299.  * Fix a path so it acts as if we have chroot.  I.e. change / to ~ftp
  300.  * and remove ..'s.
  301.  */
  302. char *fixpath(path)
  303. char *path;
  304. {
  305.     if (path[0]=='/') {
  306.     strncpy(tmpbuf,homepathbuf,TMPLEN);
  307.     strncat(tmpbuf,path,TMPLEN);
  308.     } else {
  309.     strncpy(tmpbuf,path,TMPLEN);
  310.     }
  311.     tmpbuf[TMPLEN]='\0';
  312.     return tmpbuf;
  313. }
  314.  
  315. static char tmpbuf2[TMPLEN+1];
  316. /*
  317.  * Fix a command string so it acts as if we have chroot.
  318.  */
  319. char *fixcmd(args)
  320. char *args;
  321. {
  322.     char *str = args;
  323.     char *arg;
  324.     tmpbuf2[0]='\0';
  325.     while (1) {
  326.     arg = strtok(str," \t");
  327.     if (arg==NULL) break; /* no more components */
  328.     if (str==NULL) { /* i.e. not the first time through */
  329.         strncat(tmpbuf2," ",TMPLEN);
  330.     }
  331.     str = NULL;
  332.     strncat(tmpbuf2,fixpath(arg),TMPLEN);
  333.     }
  334.     tmpbuf2[TMPLEN]='\0';
  335.     return tmpbuf2;
  336. }
  337.  
  338. retrieve(cmd, name)
  339.     char *cmd, *name;
  340. {
  341.     FILE *fin, *dout;
  342.     struct stat st;
  343.     int (*closefunc)(), tmp;
  344.     char    oldpathbuf[MAXPATHLEN+1];
  345.  
  346.     if (guest) { /* simulate chroot */
  347.         name = fixcmd(name);
  348.     }
  349.  
  350.     if (cmd == 0) {
  351. #ifdef notdef
  352.         /* no remote command execution -- it's a security hole */
  353.         if (*name == '|')
  354.             fin = popen(name + 1, "r"), closefunc = pclose;
  355.         else
  356. #endif
  357.             fin = fopen(name, "r"), closefunc = fclose;
  358.     } else {
  359.         char line[BUFSIZ];
  360.  
  361.         (void) sprintf(line, cmd, name), name = line;
  362.         fin = popen(line, "r"), closefunc = pclose;
  363.     }
  364.     if (fin == NULL) {
  365.         if (guest) {
  366.             log("Retrieve failed: %s %s\n", cmd==NULL?"fetch":cmd,
  367.                 name);
  368.         }
  369.         if (errno != 0)
  370.             reply(550, "%s: %s.", name, sys_errlist[errno]);
  371.         (void) chdir(oldpathbuf);
  372.         return;
  373.     }
  374.     (void) chdir(oldpathbuf);
  375.     st.st_size = 0;
  376.     if (cmd == 0 &&
  377.         (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
  378.         reply(550, "%s: not a plain file.", name);
  379.         goto done;
  380.     }
  381.     if (guest) {
  382.         log("Retrieve: %s %s\n", cmd==NULL?"fetch":cmd, name);
  383.     }
  384.     dout = dataconn(name, st.st_size, "w");
  385.     if (dout == NULL)
  386.         goto done;
  387.     if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) {
  388.         reply(550, "%s: %s.", name, sys_errlist[errno]);
  389.     }
  390.     else if (tmp == 0) {
  391.         reply(226, "Transfer complete.");
  392.     }
  393.     (void) fclose(dout);
  394.     data = -1;
  395.     pdata = -1;
  396. done:
  397.     (*closefunc)(fin);
  398. }
  399.  
  400. store(name, mode)
  401.     char *name, *mode;
  402. {
  403.     FILE *fout, *din;
  404.     int (*closefunc)(), dochown = 0, tmp;
  405.     char *gunique(), *local;
  406.  
  407. #ifdef notdef
  408.     /* no remote command execution -- it's a security hole */
  409.     if (name[0] == '|')
  410.         fout = popen(&name[1], "w"), closefunc = pclose;
  411.     else
  412. #endif
  413.     {
  414.         struct stat st;
  415.  
  416.         local = name;
  417.         if (stat(name, &st) < 0) {
  418.             dochown++;
  419.         }
  420.         else if (unique) {
  421.             if ((local = gunique(name)) == NULL) {
  422.                 return;
  423.             }
  424.             dochown++;
  425.         }
  426.         fout = fopen(local, mode), closefunc = fclose;
  427.     }
  428.     if (fout == NULL) {
  429.         reply(553, "%s: %s.", local, sys_errlist[errno]);
  430.         return;
  431.     }
  432.     din = dataconn(local, (off_t)-1, "r");
  433.     if (din == NULL)
  434.         goto done;
  435.     if (guest) {
  436.         log("Store %s %s\n", name, mode);
  437.     }
  438.     if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) {
  439.         reply(552, "%s: %s.", local, sys_errlist[errno]);
  440.     }
  441.     else if (tmp == 0 && !unique) {
  442.         reply(226, "Transfer complete.");
  443.     }
  444.     else if (tmp == 0 && unique) {
  445.         reply(226, "Transfer complete (unique file name:%s).", local);
  446.     }
  447.     (void) fclose(din);
  448.     data = -1;
  449.     pdata = -1;
  450. done:
  451.     if (dochown)
  452.         (void) chown(local, pw->pw_uid, -1);
  453.     (*closefunc)(fout);
  454. }
  455.  
  456. FILE *
  457. getdatasock(mode)
  458.     char *mode;
  459. {
  460.     int s, on = 1;
  461.  
  462.     if (data >= 0) {
  463.         return (fdopen(data, mode));
  464.     }
  465.     s = socket(AF_INET, SOCK_STREAM, 0);
  466.     if (s < 0) {
  467.         return (NULL);
  468.     }
  469.     seteuid(0);
  470.     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) {
  471.         goto bad;
  472.     }
  473.     /* anchor socket to avoid multi-homing problems */
  474.     data_source.sin_family = AF_INET;
  475.     data_source.sin_addr = ctrl_addr.sin_addr;
  476.     if (bind(s, &data_source, sizeof (data_source)) < 0) {
  477.         goto bad;
  478.     }
  479.     seteuid(pw->pw_uid);
  480.     return (fdopen(s, mode));
  481. bad:
  482.     seteuid(pw->pw_uid);
  483.     (void) close(s);
  484.     return (NULL);
  485. }
  486.  
  487. FILE *
  488. dataconn(name, size, mode)
  489.     char *name;
  490.     off_t size;
  491.     char *mode;
  492. {
  493.     char sizebuf[32];
  494.     FILE *file;
  495.     int retry = 0;
  496.  
  497.     if (size >= 0)
  498.         (void) sprintf (sizebuf, " (%ld bytes)", size);
  499.     else
  500.         (void) strcpy(sizebuf, "");
  501.     if (pdata > 0) {
  502.         struct sockaddr_in from;
  503.         int s, fromlen = sizeof(from);
  504.  
  505.         s = accept(pdata, &from, &fromlen);
  506.         if (s < 0) {
  507.             reply(425, "Can't open data connection.");
  508.             (void) close(pdata);
  509.             pdata = -1;
  510.             return(NULL);
  511.         }
  512.         (void) close(pdata);
  513.         pdata = s;
  514.         reply(150, "Openning data connection for %s (%s,%d)%s.",
  515.              name, inet_ntoa(from.sin_addr),
  516.              ntohs(from.sin_port), sizebuf);
  517.         return(fdopen(pdata, mode));
  518.     }
  519.     if (data >= 0) {
  520.         reply(125, "Using existing data connection for %s%s.",
  521.             name, sizebuf);
  522.         usedefault = 1;
  523.         return (fdopen(data, mode));
  524.     }
  525.     if (usedefault)
  526.         data_dest = his_addr;
  527.     usedefault = 1;
  528.     file = getdatasock(mode);
  529.     if (file == NULL) {
  530.         reply(425, "Can't create data socket (%s,%d): %s.",
  531.             inet_ntoa(data_source.sin_addr),
  532.             ntohs(data_source.sin_port),
  533.             sys_errlist[errno]);
  534.         return (NULL);
  535.     }
  536.     data = fileno(file);
  537.     while (connect(data, &data_dest, sizeof (data_dest)) < 0) {
  538.         if (errno == EADDRINUSE && retry < swaitmax) {
  539.             sleep((unsigned) swaitint);
  540.             retry += swaitint;
  541.             continue;
  542.         }
  543.         reply(425, "Can't build data connection: %s.",
  544.             sys_errlist[errno]);
  545.         (void) fclose(file);
  546.         data = -1;
  547.         return (NULL);
  548.     }
  549.     reply(150, "Opening data connection for %s (%s,%d)%s.",
  550.         name, inet_ntoa(data_dest.sin_addr),
  551.         ntohs(data_dest.sin_port), sizebuf);
  552.     return (file);
  553. }
  554.  
  555. /*
  556.  * Tranfer the contents of "instr" to
  557.  * "outstr" peer using the appropriate
  558.  * encapulation of the date subject
  559.  * to Mode, Structure, and Type.
  560.  *
  561.  * NB: Form isn't handled.
  562.  */
  563. send_data(instr, outstr)
  564.     FILE *instr, *outstr;
  565. {
  566.     register int c;
  567.     int netfd, filefd, cnt;
  568.     char buf[BUFSIZ];
  569.  
  570.     transflag++;
  571.     if (setjmp(urgcatch)) {
  572.         transflag = 0;
  573.         return(-1);
  574.     }
  575.     switch (type) {
  576.  
  577.     case TYPE_A:
  578.         while ((c = getc(instr)) != EOF) {
  579.             if (c == '\n') {
  580.                 if (ferror (outstr)) {
  581.                     transflag = 0;
  582.                     return (1);
  583.                 }
  584.                 (void) putc('\r', outstr);
  585.             }
  586.             (void) putc(c, outstr);
  587.         /*    if (c == '\r')            */
  588.         /*        putc ('\0', outstr);    */
  589.         }
  590.         transflag = 0;
  591.         if (ferror (instr) || ferror (outstr)) {
  592.             return (1);
  593.         }
  594.         return (0);
  595.         
  596.     case TYPE_I:
  597.     case TYPE_L:
  598.         netfd = fileno(outstr);
  599.         filefd = fileno(instr);
  600.  
  601.         while ((cnt = read(filefd, buf, sizeof (buf))) > 0) {
  602.             if (write(netfd, buf, cnt) < 0) {
  603.                 transflag = 0;
  604.                 return (1);
  605.             }
  606.         }
  607.         transflag = 0;
  608.         return (cnt < 0);
  609.     }
  610.     reply(550, "Unimplemented TYPE %d in send_data", type);
  611.     transflag = 0;
  612.     return (-1);
  613. }
  614.  
  615. /*
  616.  * Transfer data from peer to
  617.  * "outstr" using the appropriate
  618.  * encapulation of the data subject
  619.  * to Mode, Structure, and Type.
  620.  *
  621.  * N.B.: Form isn't handled.
  622.  */
  623. receive_data(instr, outstr)
  624.     FILE *instr, *outstr;
  625. {
  626.     register int c;
  627.     int cnt;
  628.     char buf[BUFSIZ];
  629.  
  630.  
  631.     transflag++;
  632.     if (setjmp(urgcatch)) {
  633.         transflag = 0;
  634.         return(-1);
  635.     }
  636.     switch (type) {
  637.  
  638.     case TYPE_I:
  639.     case TYPE_L:
  640.         while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
  641.             if (write(fileno(outstr), buf, cnt) < 0) {
  642.                 transflag = 0;
  643.                 return (1);
  644.             }
  645.         }
  646.         transflag = 0;
  647.         return (cnt < 0);
  648.  
  649.     case TYPE_E:
  650.         reply(553, "TYPE E not implemented.");
  651.         transflag = 0;
  652.         return (-1);
  653.  
  654.     case TYPE_A:
  655.         while ((c = getc(instr)) != EOF) {
  656.             while (c == '\r') {
  657.                 if (ferror (outstr)) {
  658.                     transflag = 0;
  659.                     return (1);
  660.                 }
  661.                 if ((c = getc(instr)) != '\n')
  662.                     (void) putc ('\r', outstr);
  663.             /*    if (c == '\0')            */
  664.             /*        continue;        */
  665.             }
  666.             (void) putc (c, outstr);
  667.         }
  668.         transflag = 0;
  669.         if (ferror (instr) || ferror (outstr))
  670.             return (1);
  671.         return (0);
  672.     }
  673.     transflag = 0;
  674.     fatal("Unknown type in receive_data.");
  675.     /*NOTREACHED*/
  676. }
  677.  
  678. fatal(s)
  679.     char *s;
  680. {
  681.     reply(451, "Error in server: %s\n", s);
  682.     reply(221, "Closing connection due to server error.");
  683.     dologout(0);
  684. }
  685.  
  686. reply(n, s, p0, p1, p2, p3, p4)
  687.     int n;
  688.     char *s;
  689. {
  690.  
  691.     printf("%d ", n);
  692.     printf(s, p0, p1, p2, p3, p4);
  693.     printf("\r\n");
  694.     (void) fflush(stdout);
  695.     if (debug) {
  696.         syslog(LOG_DEBUG, "<--- %d ", n);
  697.         syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4);
  698.     }
  699. }
  700.  
  701. lreply(n, s, p0, p1, p2, p3, p4)
  702.     int n;
  703.     char *s;
  704. {
  705.     printf("%d-", n);
  706.     printf(s, p0, p1, p2, p3, p4);
  707.     printf("\r\n");
  708.     (void) fflush(stdout);
  709.     if (debug) {
  710.         syslog(LOG_DEBUG, "<--- %d- ", n);
  711.         syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4);
  712.     }
  713. }
  714.  
  715. ack(s)
  716.     char *s;
  717. {
  718.     reply(250, "%s command successful.", s);
  719. }
  720.  
  721. nack(s)
  722.     char *s;
  723. {
  724.     reply(502, "%s command not implemented.", s);
  725. }
  726.  
  727. yyerror(s)
  728.     char *s;
  729. {
  730.     char *cp;
  731.  
  732.     cp = index(cbuf,'\n');
  733.     if (cp != NULL) {
  734.         *cp = '\0';
  735.     }
  736.     reply(500, "'%s': command not understood.",cbuf);
  737. }
  738.  
  739. delete(name)
  740.     char *name;
  741. {
  742.     struct stat st;
  743.  
  744.     if (stat(name, &st) < 0) {
  745.         reply(550, "%s: %s.", name, sys_errlist[errno]);
  746.         return;
  747.     }
  748.     if ((st.st_mode&S_IFMT) == S_IFDIR) {
  749.         if (rmdir(name) < 0) {
  750.             reply(550, "%s: %s.", name, sys_errlist[errno]);
  751.             return;
  752.         }
  753.         goto done;
  754.     }
  755.     if (unlink(name) < 0) {
  756.         reply(550, "%s: %s.", name, sys_errlist[errno]);
  757.         return;
  758.     }
  759. done:
  760.     ack("DELE");
  761. }
  762.  
  763. trych(path)
  764.     char *path;
  765. {
  766.     if (chdir(path) < 0) {
  767.         reply(550, "%s: %s.", path, sys_errlist[errno]);
  768.         return 0;
  769.     }
  770.     return 1;
  771. }
  772.  
  773. cwd(path)
  774.     char *path;
  775. {
  776.  
  777. #ifdef sprite
  778.     char    oldpathbuf[MAXPATHLEN+1];
  779.     char    pathbuf[MAXPATHLEN+1];
  780.     /*
  781.      * We don't have chroot, so we'll fake it.
  782.      */
  783.         if (guest) {
  784.         (void) getwd(oldpathbuf);    /* Remember current directory */
  785.         while (path[0]=='/') {    /* Handle root */
  786.         if (!trych(homepathbuf)) return;
  787.         path++;
  788.         }
  789.         if (!trych(path)) {        /* Now do the cd */
  790.         (void) chdir(oldpathbuf);
  791.         return;
  792.         }
  793.         (void) getwd(pathbuf);    /* Make sure we're in ftp still */
  794.         if (strncmp(pathbuf, homepathbuf, strlen(homepathbuf))) {
  795.         (void) chdir(oldpathbuf);
  796.         reply(550, "%s: Invalid path.", path);
  797.         log("Bad cwd path: %s\n", path);
  798.         return;
  799.         };
  800.         ack("CWD");
  801.         return;
  802.     }
  803. #endif
  804.     if (!trych(path)) return;
  805.     ack("CWD");
  806. }
  807.  
  808. makedir(name)
  809.     char *name;
  810. {
  811.     struct stat st;
  812.     int dochown = stat(name, &st) < 0;
  813.     
  814.     if (mkdir(name, 0777) < 0) {
  815.         reply(550, "%s: %s.", name, sys_errlist[errno]);
  816.         return;
  817.     }
  818.     if (dochown)
  819.         (void) chown(name, pw->pw_uid, -1);
  820.     reply(257, "MKD command successful.");
  821. }
  822.  
  823. removedir(name)
  824.     char *name;
  825. {
  826.  
  827.     if (rmdir(name) < 0) {
  828.         reply(550, "%s: %s.", name, sys_errlist[errno]);
  829.         return;
  830.     }
  831.     ack("RMD");
  832. }
  833.  
  834. pwd()
  835. {
  836.     char path[MAXPATHLEN + 1];
  837.  
  838.     if (getwd(path) == NULL) {
  839.         reply(550, "%s.", path);
  840.         return;
  841.     }
  842.     if (guest) {
  843.         int homelen;
  844.         homelen = strlen(homepathbuf);
  845.         if (homelen==strlen(path)) {
  846.         reply(257, "\"/\" is current directory.");
  847.         } else {
  848.         reply(257, "\"%s\" is current directory.", path+
  849.             strlen(homepathbuf));
  850.         }
  851.     } else {
  852.         reply(257, "\"%s\" is current directory.", path);
  853.     }
  854. }
  855.  
  856. char *
  857. renamefrom(name)
  858.     char *name;
  859. {
  860.     struct stat st;
  861.  
  862.     if (stat(name, &st) < 0) {
  863.         reply(550, "%s: %s.", name, sys_errlist[errno]);
  864.         return ((char *)0);
  865.     }
  866.     reply(350, "File exists, ready for destination name");
  867.     return (name);
  868. }
  869.  
  870. renamecmd(from, to)
  871.     char *from, *to;
  872. {
  873.  
  874.     if (rename(from, to) < 0) {
  875.         reply(550, "rename: %s.", sys_errlist[errno]);
  876.         return;
  877.     }
  878.     ack("RNTO");
  879. }
  880.  
  881. dolog(sin,where)
  882.     struct sockaddr_in *sin;
  883.     int where;    /* 0 = syslog, 1 = file */
  884. {
  885.     struct hostent *hp = gethostbyaddr(&sin->sin_addr,
  886.         sizeof (struct in_addr), AF_INET);
  887.     time_t t;
  888.  
  889.     if (hp) {
  890.         (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
  891.         endhostent();
  892.     } else
  893.         (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
  894.             sizeof (remotehost));
  895.     t = time((time_t *) 0);
  896.     if (where==1) {
  897.         log("Connection from %s at %s", remotehost, ctime(&t));
  898.     } else if (logging) {
  899.         syslog(LOG_INFO,"FTPD: connection from %s to %s at %s", remotehost,
  900.             hostname, ctime(&t));
  901.     }
  902. }
  903.  
  904. #include <utmp.h>
  905.  
  906. #define    SCPYN(a, b)    (void) strncpy(a, b, sizeof (a))
  907. struct    utmp utmp;
  908.  
  909. /*
  910.  * Record login in wtmp file.
  911.  */
  912. dologin(pw)
  913.     struct passwd *pw;
  914. {
  915.     char line[32];
  916.  
  917.     if (wtmp >= 0) {
  918.         /* hack, but must be unique and no tty line */
  919.         (void) sprintf(line, "ftp%d", getpid());
  920.         SCPYN(utmp.ut_line, line);
  921.         SCPYN(utmp.ut_name, pw->pw_name);
  922.         SCPYN(utmp.ut_host, remotehost);
  923.         utmp.ut_time = (long) time((time_t *) 0);
  924.         (void) write(wtmp, (char *)&utmp, sizeof (utmp));
  925.         if (!guest) {        /* anon must hang on */
  926.             (void) close(wtmp);
  927.             wtmp = -1;
  928.         }
  929.     }
  930. }
  931.  
  932. /*
  933.  * Record logout in wtmp file
  934.  * and exit with supplied status.
  935.  */
  936. dologout(status)
  937.     int status;
  938. {
  939.  
  940.     time_t t;
  941.  
  942.     if (logged_in) {
  943.         (void) seteuid(0);
  944.         if (wtmp < 0)
  945.             wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
  946.         if (wtmp >= 0) {
  947.             SCPYN(utmp.ut_name, "");
  948.             SCPYN(utmp.ut_host, "");
  949.             utmp.ut_time = (long) time((time_t *) 0);
  950.             (void) write(wtmp, (char *)&utmp, sizeof (utmp));
  951.             (void) close(wtmp);
  952.         }
  953.         if (guest) {
  954.             t = time((time_t *) 0);
  955.             log("Done anonymous ftp at %s\n", ctime(&t));
  956.         }
  957.     }
  958.     /* beware of flushing buffers after a SIGPIPE */
  959.     _exit(status);
  960. }
  961.  
  962. /*
  963.  * Special version of popen which avoids
  964.  * call to shell.  This insures noone may 
  965.  * create a pipe to a hidden program as a side
  966.  * effect of a list or dir command.
  967.  */
  968. #define    tst(a,b)    (*mode == 'r'? (b) : (a))
  969. #define    RDR    0
  970. #define    WTR    1
  971. static    int popen_pid[5];
  972.  
  973. static char *
  974. nextarg(cpp)
  975.     char *cpp;
  976. {
  977.     register char *cp = cpp;
  978.  
  979.     if (cp == 0)
  980.         return (cp);
  981.     while (*cp && *cp != ' ' && *cp != '\t')
  982.         cp++;
  983.     if (*cp == ' ' || *cp == '\t') {
  984.         *cp++ = '\0';
  985.         while (*cp == ' ' || *cp == '\t')
  986.             cp++;
  987.     }
  988.     if (cp == cpp)
  989.         return ((char *)0);
  990.     return (cp);
  991. }
  992.  
  993. FILE *
  994. popen(cmd, mode)
  995.     char *cmd, *mode;
  996. {
  997.     int p[2], ac, gac;
  998.     register myside, hisside, pid;
  999.     char *av[20], *gav[512];
  1000.     register char *cp;
  1001.  
  1002.     if (pipe(p) < 0)
  1003.         return (NULL);
  1004.     cp = cmd, ac = 0;
  1005.     /* break up string into pieces */
  1006.     do {
  1007.         av[ac++] = cp;
  1008.         cp = nextarg(cp);
  1009.     } while (cp && *cp && ac < 20);
  1010.     av[ac] = (char *)0;
  1011.     gav[0] = av[0];
  1012.     /* glob each piece */
  1013.     for (gac = ac = 1; av[ac] != NULL; ac++) {
  1014.         char **pop;
  1015.         extern char **glob(), **copyblk();
  1016.  
  1017.         pop = glob(av[ac]);
  1018.         if (pop == (char **)NULL) {    /* globbing failed */
  1019.             char *vv[2];
  1020.  
  1021.             vv[0] = av[ac];
  1022.             vv[1] = 0;
  1023.             pop = copyblk(vv);
  1024.         }
  1025.         av[ac] = (char *)pop;        /* save to free later */
  1026.         while (*pop && gac < 512)
  1027.             gav[gac++] = *pop++;
  1028.     }
  1029.     gav[gac] = (char *)0;
  1030.     myside = tst(p[WTR], p[RDR]);
  1031.     hisside = tst(p[RDR], p[WTR]);
  1032.     if ((pid = fork()) == 0) {
  1033.         /* myside and hisside reverse roles in child */
  1034.         (void) close(myside);
  1035.         (void) dup2(hisside, tst(0, 1));
  1036.         (void) close(hisside);
  1037.         execv(gav[0], gav);
  1038.         _exit(1);
  1039.     }
  1040.     for (ac = 1; av[ac] != NULL; ac++)
  1041.         blkfree((char **)av[ac]);
  1042.     if (pid == -1)
  1043.         return (NULL);
  1044.     popen_pid[myside] = pid;
  1045.     (void) close(hisside);
  1046.     return (fdopen(myside, mode));
  1047. }
  1048.  
  1049. pclose(ptr)
  1050.     FILE *ptr;
  1051. {
  1052.     register f, r, (*hstat)(), (*istat)(), (*qstat)();
  1053.     int status;
  1054.  
  1055.     f = fileno(ptr);
  1056.     (void) fclose(ptr);
  1057.     istat = signal(SIGINT, SIG_IGN);
  1058.     qstat = signal(SIGQUIT, SIG_IGN);
  1059.     hstat = signal(SIGHUP, SIG_IGN);
  1060.     while ((r = wait(&status)) != popen_pid[f] && r != -1)
  1061.         ;
  1062.     if (r == -1)
  1063.         status = -1;
  1064.     (void) signal(SIGINT, istat);
  1065.     (void) signal(SIGQUIT, qstat);
  1066.     (void) signal(SIGHUP, hstat);
  1067.     return (status);
  1068. }
  1069.  
  1070. /*
  1071.  * Check user requesting login priviledges.
  1072.  * Disallow anyone who does not have a standard
  1073.  * shell returned by getusershell() (/etc/shells).
  1074.  * Disallow anyone mentioned in the file FTPUSERS
  1075.  * to allow people such as uucp to be avoided.
  1076.  */
  1077. checkuser(name)
  1078.     register char *name;
  1079. {
  1080.     register char *cp;
  1081.     char line[BUFSIZ], *index(), *getusershell();
  1082.     FILE *fd;
  1083.     struct passwd *pw;
  1084.     int found = 0;
  1085.  
  1086.     pw = getpwnam(name);
  1087.     if (pw == NULL)
  1088.         return (0);
  1089.     if (pw ->pw_shell == NULL || pw->pw_shell[0] == NULL)
  1090.         pw->pw_shell = "/bin/sh";
  1091.     while ((cp = getusershell()) != NULL)
  1092.         if (strcmp(cp, pw->pw_shell) == 0)
  1093.             break;
  1094.     endusershell();
  1095.     if (cp == NULL)
  1096.         return (0);
  1097.     fd = fopen(FTPUSERS, "r");
  1098.     if (fd == NULL)
  1099.         return (1);
  1100.     while (fgets(line, sizeof (line), fd) != NULL) {
  1101.         cp = index(line, '\n');
  1102.         if (cp)
  1103.             *cp = '\0';
  1104.         if (strcmp(line, name) == 0) {
  1105.             found++;
  1106.             break;
  1107.         }
  1108.     }
  1109.     (void) fclose(fd);
  1110.     return (!found);
  1111. }
  1112.  
  1113. myoob()
  1114. {
  1115.     char *cp;
  1116.  
  1117.     /* only process if transfer occurring */
  1118.     if (!transflag) {
  1119.         return;
  1120.     }
  1121.     cp = tmpline;
  1122.     if (getline(cp, 7, stdin) == NULL) {
  1123.         reply(221, "You could at least say goodby.");
  1124.         dologout(0);
  1125.     }
  1126.     upper(cp);
  1127.     if (strcmp(cp, "ABOR\r\n"))
  1128.         return;
  1129.     tmpline[0] = '\0';
  1130.     reply(426,"Transfer aborted. Data connection closed.");
  1131.     reply(226,"Abort successful");
  1132.     longjmp(urgcatch, 1);
  1133. }
  1134.  
  1135. /*
  1136.  * Note: The 530 reply codes could be 4xx codes, except nothing is
  1137.  * given in the state tables except 421 which implies an exit.  (RFC959)
  1138.  */
  1139. passive()
  1140. {
  1141.     int len;
  1142.     struct sockaddr_in tmp;
  1143.     register char *p, *a;
  1144.  
  1145.     pdata = socket(AF_INET, SOCK_STREAM, 0);
  1146.     if (pdata < 0) {
  1147.         reply(530, "Can't open passive connection");
  1148.         return;
  1149.     }
  1150.     tmp = ctrl_addr;
  1151.     tmp.sin_port = 0;
  1152.     seteuid(0);
  1153.     if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
  1154.         seteuid(pw->pw_uid);
  1155.         (void) close(pdata);
  1156.         pdata = -1;
  1157.         reply(530, "Can't open passive connection");
  1158.         return;
  1159.     }
  1160.     seteuid(pw->pw_uid);
  1161.     len = sizeof(tmp);
  1162.     if (getsockname(pdata, (char *) &tmp, &len) < 0) {
  1163.         (void) close(pdata);
  1164.         pdata = -1;
  1165.         reply(530, "Can't open passive connection");
  1166.         return;
  1167.     }
  1168.     if (listen(pdata, 1) < 0) {
  1169.         (void) close(pdata);
  1170.         pdata = -1;
  1171.         reply(530, "Can't open passive connection");
  1172.         return;
  1173.     }
  1174.     a = (char *) &tmp.sin_addr;
  1175.     p = (char *) &tmp.sin_port;
  1176.  
  1177. #define UC(b) (((int) b) & 0xff)
  1178.  
  1179.     reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
  1180.         UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
  1181. }
  1182.  
  1183. char *
  1184. gunique(local)
  1185.     char *local;
  1186. {
  1187.     static char new[MAXPATHLEN];
  1188.     char *cp = rindex(local, '/');
  1189.     int d, count=0;
  1190.     char ext = '1';
  1191.  
  1192.     if (cp) {
  1193.         *cp = '\0';
  1194.     }
  1195.     d = access(cp ? local : ".", 2);
  1196.     if (cp) {
  1197.         *cp = '/';
  1198.     }
  1199.     if (d < 0) {
  1200.         syslog(LOG_ERR, "%s: %m", local);
  1201.         return((char *) 0);
  1202.     }
  1203.     (void) strcpy(new, local);
  1204.     cp = new + strlen(new);
  1205.     *cp++ = '.';
  1206.     while (!d) {
  1207.         if (++count == 100) {
  1208.             reply(452, "Unique file name not cannot be created.");
  1209.             return((char *) 0);
  1210.         }
  1211.         *cp++ = ext;
  1212.         *cp = '\0';
  1213.         if (ext == '9') {
  1214.             ext = '0';
  1215.         }
  1216.         else {
  1217.             ext++;
  1218.         }
  1219.         if ((d = access(new, 0)) < 0) {
  1220.             break;
  1221.         }
  1222.         if (ext != '0') {
  1223.             cp--;
  1224.         }
  1225.         else if (*(cp - 2) == '.') {
  1226.             *(cp - 1) = '1';
  1227.         }
  1228.         else {
  1229.             *(cp - 2) = *(cp - 2) + 1;
  1230.             cp--;
  1231.         }
  1232.     }
  1233.     return(new);
  1234. }
  1235.  
  1236. /*
  1237.  * log an error message
  1238.  *
  1239.  */
  1240. void
  1241. log(va_alist)
  1242.     va_dcl
  1243. {
  1244.         FILE *fp;
  1245.         char *format;
  1246.         va_list args;
  1247.     int euid;
  1248.  
  1249.     euid = geteuid();
  1250.     (void) seteuid(0);
  1251.  
  1252.         va_start(args);
  1253.         if ((fp = fopen(ftpdlog, "a+")) == NULL) {
  1254.         (void) seteuid(euid);
  1255.                 return;
  1256.     }
  1257.         format = va_arg(args, char *);
  1258.         vfprintf(fp, format, args);
  1259.         fclose(fp);
  1260.     (void) seteuid(euid);
  1261.         return;
  1262. }
  1263.